/**
 *  Copyright 2013 University Pierre & Marie Curie - UMR CNRS 7606 (LIP6/MoVe)
 *  All rights reserved.   This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Initial contributor:
 *    Jean-Luc Amitousa - Thanh Cong - <jeanluc.amitousa.mankoy@gmail.com>
 *
 *  Mailing list:
 *    lom-messan.hillah@lip6.fr
 */
package sdmakerz_v1.coresyntax;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;

import sdmakerz_v1.coresyntax.generator.interfaces.IInputData;
import sdmakerz_v1.coresyntax.generator.interfaces.IInterfaceData;
import sdmakerz_v1.coresyntax.io.SDMakerIO;
import sdmakerz_v1.coresyntax.sDMakerV1.BooleanExp;
import sdmakerz_v1.coresyntax.sDMakerV1.Call;
import sdmakerz_v1.coresyntax.sDMakerV1.CharExp;
import sdmakerz_v1.coresyntax.sDMakerV1.Diagram;
import sdmakerz_v1.coresyntax.sDMakerV1.DoubleExp;
import sdmakerz_v1.coresyntax.sDMakerV1.Expression;
import sdmakerz_v1.coresyntax.sDMakerV1.ImportType;
import sdmakerz_v1.coresyntax.sDMakerV1.IntegerExp;
import sdmakerz_v1.coresyntax.sDMakerV1.Lifeline;
import sdmakerz_v1.coresyntax.sDMakerV1.Obj;
import sdmakerz_v1.coresyntax.sDMakerV1.ObjIdentifierExp;
import sdmakerz_v1.coresyntax.sDMakerV1.SDMakerModel;
import sdmakerz_v1.coresyntax.sDMakerV1.StringExp;

public class SDMakerV1Utils {

	/**
	 * Retrouve le premier container du type desire.
	 * @param contained l'objet contenant
	 * @param wantedClass le type du container desire.
	 * @return le container si il existe, null sinon
	 */
	public static 
	EObject findContainer(EObject contained, Class wantedClass){

		EObject container= contained;
		boolean found= false;

		if(contained!=null){
			do{
				container= container.eContainer();
				found= wantedClass.isInstance(container);
			}while(found==false&&
					container!=null&&
					container.getClass()
					.getSimpleName()
					.compareTo(Object.class.getSimpleName())!=0);
		}

		return container;
	}

//	public static ModelKind modelKind(Resource input){
//		ModelKind modelKind= null;
//		TreeIterator<EObject> content= input.getAllContents();
//
//		while(content.hasNext()){
//			EObject eo= content.next();
//
//			if(eo instanceof Model){
//				Model model= (Model)eo;
//
//				if(model.getContent() instanceof SDMakerModel){
//					modelKind= ModelKind.SD_MAKER_MODEL;
//					break;
//				}
//				else if (model.getContent() instanceof SDEditModel){
//					modelKind= ModelKind.SD_EDIT_MODEL;
//					break;
//				}
//			}
//		}
//
//		return modelKind;
//	}

	public static boolean contains(List<String>container, String str){
		boolean contains= false;

		for(String strTmp : container){
			if(str.compareTo(strTmp)==0){
				contains= true;
			}
		}

		return contains;
	}

	public static boolean contains(List<Resource>container, Resource input){
		boolean contains= false;

		for(Resource inputTmp : container){
			String str1= input.getURI().toString();
			String str2= inputTmp.getURI().toString();

			if(str1.compareTo(str2)==0){
				contains= true;
			}
		}

		return contains;
	} 

	public static 
	boolean contains(List<IInterfaceData>container, IInterfaceData iid){
		boolean contains= false;

		for(IInterfaceData iidTmp : container){
			String str1= iid.getInterfaceID();
			String str2= iidTmp.getInterfaceID();

			if(str1.compareTo(str2)==0){
				contains= true;
			}
		}

		return contains;
	} 

	//	public static 
	//	boolean contains(List<ModelKind> container, ModelKind mk){
	//		
	//	}

	//	/**
	//	 * Retrouver toutes les declarations visibles
	//	 * @Hypothese la resource represente un SDMakerModel
	//	 * @param input la resource contenant le model.
	//	 * @return une list des declarations
	//	 * @throws IOException 
	//	 * @throws Exception 
	//	 */
	//	public static List<Declaration> 
	//	getAllDeclarations() throws IOException{
	//		SDMakerIO sdmakerIO= new SDMakerIO();
	//		List<InputData> inputDatas= sdmakerIO.allInputs();
	//		List<Declaration> allDeclaration= new ArrayList<Declaration>();
	//
	//		
	//		for(InputData inputDTmp: inputDatas){
	//			Resource inputTmp= inputDTmp.getInput();
	//			
	//			TreeIterator<EObject> allContent= inputTmp.getAllContents();
	//			while(allContent.hasNext()){
	//
	//				EObject tmp= allContent.next();
	//				if(tmp instanceof Declaration){
	//					allDeclaration.add((Declaration)tmp);
	//				}
	//			}
	//		}
	//
	//		return allDeclaration;
	//	}
	//	
	//	/**
	//	 * Retrouver toutes les declarations visibles
	//	 * @Hypothese la resource represente un SDMakerModel
	//	 * @param input la resource contenant le model.
	//	 * @return une list des declarations
	//	 * @throws IOException 
	//	 * @throws Exception 
	//	 */
	//	public static List<Call> 
	//	getAllCalls() throws IOException{
	//		SDMakerIO sdmakerIO= new SDMakerIO();
	//		List<InputData> inputDatas= sdmakerIO.allInputs();
	//		List<Call> allCalls= new ArrayList<Call>();
	//
	//		
	//		for(InputData inputDTmp: inputDatas){
	//			Resource inputTmp= inputDTmp.getInput();
	//			
	//			TreeIterator<EObject> allContent= inputTmp.getAllContents();
	//			while(allContent.hasNext()){
	//
	//				EObject tmp= allContent.next();
	//				if(tmp instanceof Call){
	//					allCalls.add((Call)tmp);
	//				}
	//			}
	//		}
	//
	//		return allCalls;
	//	}

	public static List<EObject> findAll(Class clazz) 
			throws IOException,
			InstantiationException, IllegalAccessException{

		SDMakerIO sdmakerIO= new SDMakerIO();
		List<IInputData> inputDatas= sdmakerIO.allInputs();
		List<EObject> founded= new ArrayList<EObject>();


		for(IInputData inputDTmp: inputDatas){
			Resource inputTmp= inputDTmp.getInput();

			TreeIterator<EObject> allContent= inputTmp.getAllContents();
			while(allContent.hasNext()){

				EObject tmp= allContent.next();
				if(clazz.isInstance(tmp)){
					founded.add(tmp);
				}
			}
		}

		return founded;
	}

	//	public static List<Declaration> 
	//	getAllDeclarations() throws IOException{
	//		List<EObject> matchingItems= findAll(Declaration.class);
	//		List<Declaration> declarations= new ArrayList<Declaration>();
	//
	//		for(EObject eo : matchingItems){
	//			declarations.add((Declaration)eo);
	//		}
	//
	//		return declarations;
	//	}

	public static List<Call> getAllCalls() 
			throws IOException, 
			InstantiationException, IllegalAccessException{

		List<EObject> matchingItems= findAll(Call.class);
		List<Call> calls= new ArrayList<Call>();

		for(EObject eo : matchingItems){
			calls.add((Call)eo);
		}

		return calls;
	}

	public static List<Obj> getAllObjs() 
			throws IOException, 
			InstantiationException, IllegalAccessException {

		List<EObject> matchingItems= findAll(Obj.class);
		List<Obj> objs= new ArrayList<Obj>();

		for(EObject eo : matchingItems){
			objs.add((Obj)eo);
		}

		return objs;
	}

	public static boolean isQualifiedName(String str){
		return str.split("\\.").length > 1;
	}

	public static String getQualifiedName(Resource input, String name){
		String qualifiedName= null;
		List<String> matchingQualifiedName= findMatchingQualifiedNames(input, name);

		//!= 1 => pas trouve ou conflit
		if(matchingQualifiedName.size() == 1){
			qualifiedName= matchingQualifiedName.get(0);
		}

		return qualifiedName;
	}

	public static List<String> 
	findMatchingQualifiedNames(Resource input, String name){
		List<String> matchingQualifiedName= new ArrayList<String>();
		TreeIterator<EObject> allContentIT= input.getAllContents();

		if(SDMakerV1Utils.isQualifiedName(name)){
			matchingQualifiedName.add(name);
		}
		else{
			while(allContentIT.hasNext()){
				EObject eo= allContentIT.next();

				if(eo instanceof ImportType){
					ImportType imp= (ImportType)eo;

					String[] splitedIMP= imp.getName().split("\\.");

					if(splitedIMP.length > 0){
						String impTypeSimpleName=
								splitedIMP[splitedIMP.length-1];
						if(impTypeSimpleName.compareTo(name)==0){
							matchingQualifiedName.add(imp.getName());
						}
					}
				}
			}
		}

		return matchingQualifiedName;
	}

	public static String
	clazzQualifiedNameFromExp(IInputData inputData, Expression exp){
		String clazzQN= Object.class.getName();

		if(exp instanceof BooleanExp){
			clazzQN= Boolean.class.getName();
		}
		else if(exp instanceof IntegerExp){
			clazzQN= Integer.class.getName();
		}
		else if(exp instanceof DoubleExp){
			clazzQN= Double.class.getName();
		}
		else if(exp instanceof CharExp){
			clazzQN= Character.class.getName();
		}
		else if(exp instanceof StringExp){
			clazzQN= String.class.getName();
		}
		else if(exp instanceof ObjIdentifierExp){
			clazzQN= clazzQualifiedNameFromObjIdentifier(
					inputData, (ObjIdentifierExp)exp);
		}

		return clazzQN;
	}

	public static Obj objFromIdentifier(EObject container, String identifier){
		EObject eo= findContainer(container, Diagram.class);
		EObject eoTmp= null;
		Diagram d= (Diagram)eo;
		TreeIterator<EObject> dContents= null;
		Obj objFromIdentifier= null;

		if(d != null){
			dContents= d.eAllContents();

			while(dContents.hasNext()){
				eoTmp= dContents.next();

				if(eoTmp instanceof Obj){
					Obj objTmp= (Obj)eoTmp;

					if(objTmp.getName().compareTo(identifier)==0){

						objFromIdentifier= (Obj)eoTmp;
						break;
					}
				}
			}
		}

		return objFromIdentifier;
	}

	public static String
	clazzQualifiedNameFromObjIdentifier(
			IInputData inputData, ObjIdentifierExp exp){

		String clazzQN= Object.class.getName();
		Obj objFromIdentifier= objFromIdentifier(exp, exp.getValue());
		List<String> matchingQualifiedNames= null;
		System.out.println("--->WANNA "+exp.getValue()+" in "+inputData.getInput().getURI());


		if(objFromIdentifier != null){
			matchingQualifiedNames= 
					findMatchingQualifiedNames(
							inputData.getInput(), 
							objFromIdentifier.getType().getIdentifier());
			if(matchingQualifiedNames.size() == 1){
				clazzQN= matchingQualifiedNames.get(0);
			}
		}

		return clazzQN;
	}

	public static boolean sameClass(Class c1, Class c2){
		return c1.getName().compareTo(c2.getName())==0;
	}

	public static
	boolean isSubClass(Class container, Class contained){
		Class containerTMP= contained;
		boolean found= false;

		if(contained!=null){
			do{
				containerTMP= containerTMP.getSuperclass();
				found= 
						containerTMP!=null&&
						sameClass(container, containerTMP);
			}while(found==false&&
					containerTMP!=null&&
					sameClass(containerTMP, Object.class)==false);
		}

		return found;
	}

	public static Class getMergedClass(List<Class<?> > clazzs){
		Class mergedClass= null;

		if(clazzs.size() > 0){
			mergedClass= clazzs.get(0);

			for(Class clazz : clazzs){
				if(isSubClass(clazz, mergedClass)){
					mergedClass= clazz;
				} 
				else if (isSubClass(mergedClass, clazz)==false){
					mergedClass= Object.class;
				}
			}
		}

		return mergedClass;
	}

	/**
	 * QualifiedName's packageName consist of the numberOfFragment - 1 fragments
	 * that compose it.
	 */
	public static String computePackageName(String qualifiedName){
		String packageName= null;
		String[] splitedQN= null;

		if(qualifiedName != null){
			packageName= "";
			splitedQN= qualifiedName.split("\\.");

			if(splitedQN.length > 1){
				for(int i= 0; i < (splitedQN.length - 2); i++){
					packageName += splitedQN[i] + ".";
				}

				packageName += splitedQN[splitedQN.length - 2];
			}
		}

		return packageName;
	}

	/**
	 * QualifiedName's simpleName consist of the last fragment that compose it.
	 */
	public static 
	String computeSimpleName(String qualifiedName){
		String interfaceName= null;
		String[] splitedQN= null;

		if(qualifiedName != null){
			splitedQN= qualifiedName.split("\\.");
			interfaceName= splitedQN[splitedQN.length - 1];
		}

		return interfaceName;
	}

	public static Lifeline findLifeline(Diagram d, String name){
		System.out.println("findObj: begin");
		Lifeline lifeline= null;
		TreeIterator<EObject> diagContentIT= d.eAllContents();

		while(diagContentIT.hasNext()){
			EObject eo= diagContentIT.next();

			if(eo instanceof Lifeline){
				Lifeline lifelineTmp= (Lifeline)eo;

				if(lifelineTmp.getName().compareTo(name)==0){
					lifeline= lifelineTmp;
					break;
				}
			}
		}
		System.out.println("findObj: end");
		return lifeline;
	}

	public static <T> List<T> getAll(
			Collection< ? extends EObject> collection, 
			Class< ? extends T> clazz){
		List<T> all= new ArrayList<T>();

		for(EObject eo : collection){
			if(clazz.isInstance(eo)){
				all.add(clazz.cast(eo));
			}
		}

		return all;
	}
}
